package com.hydra.file.services.impl;

import com.hydra.base.error.BusinessException;
import com.hydra.base.utils.FormatUtil;
import com.hydra.base.utils.HttpUtils;
import com.hydra.base.utils.OperateSystemUtil;
import com.hydra.base.utils.SnowIdUtil;
import com.hydra.file.constant.FileConstant;
import com.hydra.file.entity.SysFile;
import com.hydra.file.services.FileService;
import com.hydra.file.services.SysFileRepository;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.text.SimpleDateFormat;
import java.util.*;

@Slf4j
@Service
public class FileServiceImpl implements FileService {

	@Value("${os.window.path}")
	private String osWindowPath;
	@Value("${os.linux.path}")
	private String osLinuxPath;

	@Value("${spring.cloud.client.ip-address}")
	private String ipAddress;

	@Value("${server.port}")
	private String port;

	@Resource
	private SysFileRepository sysFileRepository;

	//格式化日期作为文件夹
	private final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

	/**
	 * 下载图片
	 */
	@Override
	public String downloadFile(String url, SysFile sysFile) throws Exception {
		String path = this.generateFullPath(sysFile);

		//拆分URL
		URI uri = new URI(url);
		Map<String, String> headers = new HashMap<>();
		headers.put("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36");
		headers.put("Accept", "*/*");
		String host = uri.getHost();
		headers.put("Host", host);
		headers.put("Referer", host);
		headers.put("Accept-Encoding", "gzip, deflate, br");
		headers.put("Connection", "keep-alive");

		HttpUtils.getImageInputStream(url, path, headers, null);
		return path;
	}

	/**
	 * 保存文件文件
	 * @param is 输入流
	 * @return 文件保存的路径
	 */
	@Override
	public String persistFile(InputStream is, SysFile sysFile) throws Exception {
		String path = this.generateFullPath(sysFile);
		HttpUtils.saveFileInputStream(is, path);
		return path;
	}

	/**
	 * 组装文件
	 * @param url		网络文件全路径
	 * @param fileName	文件名，为空，表示需要设置为uuid
	 */
	@Override
	public SysFile createSysFile(String url, String fileName, String fileSuffix, String filePath, String type){
		SysFile sysFile = new SysFile();
		//不能在这里设置id，会导致保存前后id不一致
//		sysFile.setId(SnowIdUtil.getInstance().nextId());
		sysFile.setUrl(url);
		sysFile.setServerIp(ipAddress);
		sysFile.setServerPort(port);
		sysFile.setFilePath(filePath);
		sysFile.setFileType(type);

		//获取文件名,为空则设置为uuid
		if(FormatUtil.isEmpty(fileName)){
			fileName = UUID.randomUUID().toString();
		}
		//从url中获取文件名
		String originName = url.substring(url.lastIndexOf("/")+1);
		//获取存在后缀，直接使用，不存在则从url中截取后缀
		if(FormatUtil.isNotEmpty(fileSuffix)){
			fileName += fileSuffix;
		}else if(FormatUtil.isNotNull(originName) && originName.contains(".")){
			String suffix = originName.substring(originName.lastIndexOf("."));
			fileName += suffix;
		}else{//没有设置后缀，url中也没有一个点，则根据文件类型设置后缀
			if(FileConstant.FILE_TYPE_IMAGE.equals(type)){
				fileName += ".png";
			}
		}
		sysFile.setFileName(fileName);
		return sysFile;
	}

	/**
	 * 获取文件本地
	 * @param id	文件id
	 */
	@SneakyThrows
	@Override
	public byte[] getSysFileAsBytes(Long id) {
		Optional<SysFile> sysFileOptional = sysFileRepository.getOptById(id);
		SysFile sysFile;
		if(sysFileOptional.isPresent()){
			sysFile = sysFileOptional.get();
			String filePath = this.getFilePath(sysFile);
			File file = new File(filePath);
			if(!file.exists()){
				throw new BusinessException("读取文件失败！文件不存在！文件id："+id);
			}
			try (FileInputStream inputStream = new FileInputStream(file)) {
				byte[] bytes = new byte[inputStream.available()];
				int read = inputStream.read(bytes, 0, inputStream.available());
				if (read == -1) {
					throw new BusinessException("读取文件失败！文件内容为空！文件id：" + id);
				}
				inputStream.close();
				return bytes;
			} catch (IOException e) {
				throw new BusinessException(e.getMessage(), e);
			}
		}else{
			throw new BusinessException("读取文件失败！文件id："+id);
		}
	}


	/**
	 * 删除文件
	 * @param id	文件id
	 */
	@Override
	public SysFile deleteSysFile(Long id) {
		Optional<SysFile> sysFileOptional = sysFileRepository.getOptById(id);
		SysFile sysFile;
		if(sysFileOptional.isPresent()){
			sysFile = sysFileOptional.get();
			String filePath = this.getFilePath(sysFile);
			File file = new File(filePath);
			if(file.exists()){//文件存在才需要去删除
				boolean delete = file.delete();
				if(!delete){//文件被占用的情况下会删除失败
					throw new BusinessException("文件删除失败！文件id："+id);
				}
			}
			//判断父文件夹是否为空，为空则删除
			File parentFile = file.getParentFile();
			if(parentFile.exists()){
				String[] fileList = parentFile.list();
				if(fileList == null || fileList.length == 0){
					boolean deleteParent = parentFile.delete();
					if(!deleteParent){
						throw new BusinessException("文件夹为空，删除父文件失败！文件id："+id);
					}
				}
			}
			sysFileRepository.removeById(sysFile);
		}else{
			throw new BusinessException("删除文件失败！文件id："+id);
		}
		return sysFile;
	}

	/**
	 * 检查文件是否存在
	 */
	@Override
	public boolean moveFileOrDelete(Long id, boolean delete, String remark, String movePath) {
		Optional<SysFile> sysFileOptional = sysFileRepository.getOptById(id);
		SysFile sysFile;
		if(sysFileOptional.isPresent()){
			sysFile = sysFileOptional.get();
			sysFile.setRemark(remark);
			String filePath = this.getFilePath(sysFile);
			File file = new File(filePath);
			boolean exists = file.exists();
			if(delete && !exists){
				sysFileRepository.removeById(sysFile);
			}
			if(exists && FormatUtil.isNotEmpty(movePath)){
				//移动路径
				sysFile.setFilePath(movePath);
				String path = this.generateFullPath(sysFile);

//				log.info("当前路径："+filePath+"，移动路径："+path);
				File newFIle = new File(path);
				File parentFile = newFIle.getParentFile();
				if(!parentFile.exists()){
					if(!parentFile.mkdirs()){
						throw new BusinessException("创建移动目标的父文件夹失败："+parentFile.getAbsolutePath());
					}
				}
				boolean result = file.renameTo(newFIle);
				if(result){
					log.info("文件移动成功："+path);
					String curFilePath = this.generateCurrentPath(sysFile.getFileName(), sysFile.getFilePath());
					sysFile.setPath(curFilePath);
					sysFileRepository.save(sysFile);
				}else{
					throw new BusinessException("移动文件失败！path："+path);
				}
			}

			return exists;
		}
		return false;
	}

	/**
	 * 获取根路径地址
	 * 主要是为了区分windows、linux
	 */
	private String getRootPath(){
		String rootPath = OperateSystemUtil.isWindows() ?osWindowPath :osLinuxPath;
		if(!rootPath.endsWith("/")){
			rootPath += "/";
		}
		return rootPath;
	}

	/**
	 * 生成当前的保存的路径，主要是以日期为主
	 * @param filePath 文件的中间路径，为空则使用当前日期
	 */
	private String generateCurrentPath(String fileName, String filePath){
		String curFilePath = "";
		if(FormatUtil.isEmpty(filePath)){
			filePath = sdf.format(new Date());
		}
		curFilePath += filePath + "/" + fileName;
		return "/"+curFilePath;
	}

	/**
	 * 生成全路径
	 */
	private String generateFullPath(SysFile sysFile){
		String rootPath = this.getRootPath();
		String curFilePath = this.generateCurrentPath(sysFile.getFileName(), sysFile.getFilePath());
		sysFile.setPath(curFilePath);
		return rootPath + sysFile.getFileType() + curFilePath;
	}

	/**
	 * 获取全路径
	 */
	private String getFilePath(SysFile sysFile){
		String rootPath = this.getRootPath();
		String path = sysFile.getPath();
		return rootPath + sysFile.getFileType() + path;
	}

}
